﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.VisualBasic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using jaysJourney_v01.States;
using jaysJourney_v01.Common;
using jaysJourney_v01.Utils.Blocks.Interactive;
using jaysJourney_v01.Utils.Blocks.Interactive.Types;

namespace jaysJourney_v01.Utils
{
    class LevelBuilder
    {
        /**
         * MEMBER VARIABLES
         * */

        // from Parent

        private PlayState state;
        private SpriteBatch mainSpriteBatch;

        // Common

        public int XCOUNT;
        public int YCOUNT;
        public int SETTING = 1;

        private static int NUM_FIRSTBLOCK = 8;
        private static int NUM_BLOCKSIZE = 2;


        private const int NUM_SETTINGS = 2;
        private const int NUM_ANIMATIONS = 5;
        private const int NUM_PROPERTIES = 4;
        private float[, ,] Animationprops;
        public GoalBlock goal;

        // Textures & Animations

        private Texture2D s1SimpleBlockTexture;
        //private Texture2D s1GoalTexture;

        // World

        public String curWorldData;

        public FieldType[,] curLevel { get; private set; }
        public Vector2 StartPoint { get; private set; }
        public Vector2 Goal { get; private set; }

        /**
         * METHODS
         * */

        // Standard Constructor

        public LevelBuilder(PlayState s, SpriteBatch b)
        {
            this.state = s;
            this.mainSpriteBatch = b;
            this.initAnimations();
            this.initTexturesAnimations();
        }

        // Constructor loading world

        public LevelBuilder(PlayState s, SpriteBatch b, string worldName) : this(s, b)
        {
            loadWorld(worldName); 
        }

        

        private void initAnimations()
        {
            Animationprops = new float[NUM_SETTINGS, NUM_ANIMATIONS, NUM_PROPERTIES];


            //SETTING 1

            //animation 1 - dissolv idle
            Animationprops[0,0,0] = 0.5f; //fps
            Animationprops[0,0,1] = 2;  //total frames
            Animationprops[0,0,2] = 2;  //sheet width  
            Animationprops[0,0,3] = 1;  //sheet height

            //animation 2 - dissolv trigger
            Animationprops[0,1,0] = 24;
            Animationprops[0,1,1] = 16;
            Animationprops[0,1,2] = 8;
            Animationprops[0,1,3] = 2;

            //animation 3 - deadly block
            Animationprops[0,2,0] = 12;
            Animationprops[0,2,1] = 4;
            Animationprops[0,2,2] = 4;
            Animationprops[0,2,3] = 1;

            //animation 4 - invis idle
            Animationprops[0,3,0] = 12;
            Animationprops[0,3,1] = 15;
            Animationprops[0,3,2] = 15;
            Animationprops[0,3,3] = 1;

            //animation 5 - invis trigger
            Animationprops[0,4,0] = 10f;
            Animationprops[0,4,1] = 18;
            Animationprops[0,4,2] = 18;
            Animationprops[0,4,3] = 1;

            //SETTING 2

            //animation 1 - dissolv idle
            Animationprops[1, 0, 0] = 0.5f;
            Animationprops[1, 0, 1] = 1;
            Animationprops[1, 0, 2] = 1;
            Animationprops[1, 0, 3] = 1;

            //animation 2 - dissolv trigger
            Animationprops[1, 1, 0] = 24;
            Animationprops[1, 1, 1] = 16;
            Animationprops[1, 1, 2] = 8;
            Animationprops[1, 1, 3] = 2;

            //animation 3 - deadly block
            Animationprops[1, 2, 0] = 12;
            Animationprops[1, 2, 1] = 20;
            Animationprops[1, 2, 2] = 20;
            Animationprops[1, 2, 3] = 1;

            //animation 4 - invis idle
            Animationprops[1, 3, 0] = 12;
            Animationprops[1, 3, 1] = 16;
            Animationprops[1, 3, 2] = 16;
            Animationprops[1, 3, 3] = 1;

            //animation 5 - invis trigger
            Animationprops[1, 4, 0] = 12;
            Animationprops[1, 4, 1] = 22;
            Animationprops[1, 4, 2] = 22;
            Animationprops[1, 4, 3] = 1;
        }

        // Load the World
        public void loadWorld(string worldName)
        {
            curWorldData = WebLoader.loadWorld(worldName);
            curWorldData.Replace(System.Environment.NewLine, String.Empty);

            splitStringtoWorld();
            
        }

        // split string into worldArray
        public void splitStringtoWorld()
        {
            this.state.interactiveBlocks.emptyBlockList();

            Int32.TryParse(curWorldData.Substring(0, 3), out XCOUNT);
            Int32.TryParse(curWorldData.Substring(3, 3), out YCOUNT);
            Int32.TryParse(curWorldData.Substring(6, 2), out SETTING);
            
            Random rnd = new Random();  

            curLevel = new FieldType[XCOUNT, YCOUNT];

            for (int x = 0; x < XCOUNT; x++)
            {
                for(int y = 0; y < YCOUNT; y++)
                {
                    switch (curWorldData.Substring(NUM_FIRSTBLOCK + (x * YCOUNT * NUM_BLOCKSIZE) + (y * NUM_BLOCKSIZE), NUM_BLOCKSIZE))
                    {
                        case "00": curLevel[x, y] = FieldType.Nothing; break;
                        case "01":
                            curLevel[x, y] = FieldType.Startpoint;
                            StartPoint = new Vector2(x, y - 1);
                            break;
                        case "02":
                            curLevel[x, y] = FieldType.Goal;
                            this.Goal = new Vector2(x, y);
                            SpriteAnimator goalIdle = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Universal\goal_idle"), 12f, 16, CONST.BLOCKSIZE, CONST.BLOCKSIZE * 2, CONST.BLOCKSIZE * 16, CONST.BLOCKSIZE, true, true);
                            SpriteAnimator goalTriggered = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Universal\goal_triggered"), 12f, 8, CONST.BLOCKSIZE, CONST.BLOCKSIZE * 2, CONST.BLOCKSIZE * 8, CONST.BLOCKSIZE, false, true);
                            goal = new GoalBlock(this.mainSpriteBatch, new Vector2(x * CONST.BLOCKSIZE, y * CONST.BLOCKSIZE - CONST.BLOCKSIZE), new Vector2(CONST.BLOCKSIZE, CONST.BLOCKSIZE), goalIdle, goalTriggered);
                            this.state.interactiveBlocks.addBlock(goal);
                            break;
                        case "10":
                            int num = rnd.Next(1, 100);
                            if (num <=80) curLevel[x, y] = FieldType.simpleBlock1;
                            else if (num > 80 && num <= 90) curLevel[x, y] = FieldType.simpleBlock2;
                            else curLevel[x, y] = FieldType.simpleBlock3;
                            break;
                        case "11":
                            curLevel[x, y] = FieldType.dissolvingBlock;
                            SpriteAnimator s1DissolvingIdle = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\dissolvingblock"), Animationprops[SETTING-1, 0, 0], (int)Animationprops[SETTING-1, 0, 1], CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 0, 2], CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 0, 3], true, false);
                            SpriteAnimator s1DissolvingTrigger = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\dissolvingblock"), Animationprops[SETTING-1, 1, 0], (int)Animationprops[SETTING-1, 1, 1], CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 1, 2], CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 1, 3], false, false);
                            this.state.interactiveBlocks.addBlock(new DissolvingBlock(this.mainSpriteBatch, new Vector2(x * CONST.BLOCKSIZE, y * CONST.BLOCKSIZE), new Vector2(CONST.BLOCKSIZE, CONST.BLOCKSIZE), s1DissolvingIdle, s1DissolvingTrigger));
                            break;
                        case "12":
                            curLevel[x, y] = FieldType.spikeBlock;
                            SpriteAnimator s1SpikeIdle = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\spikeblock"), Animationprops[SETTING-1, 2, 0], (int)Animationprops[SETTING-1, 2, 1], CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 2, 2], CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 2, 3], true, false);
                            this.state.interactiveBlocks.addBlock(new SpikeBlock(this.mainSpriteBatch, new Vector2(x * CONST.BLOCKSIZE, y * CONST.BLOCKSIZE), new Vector2(CONST.BLOCKSIZE, CONST.BLOCKSIZE), s1SpikeIdle, s1SpikeIdle));
                            break;
                        case "13":
                            curLevel[x, y] = FieldType.invisBlock;
                            SpriteAnimator s1InvisBlockIdle = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\invisblock"), Animationprops[SETTING-1, 3, 0], (int)Animationprops[SETTING-1, 3, 1], CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 3, 2], CONST.BLOCKSIZE * (int)Animationprops[SETTING-1, 3, 3], true, false);
                            SpriteAnimator s1InvisBlockTriggered = new SpriteAnimator(this.mainSpriteBatch, this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\invisblock_trig"), Animationprops[SETTING - 1, 4, 0], (int)Animationprops[SETTING - 1, 4, 1], CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * (int)Animationprops[SETTING - 1, 4, 2], CONST.BLOCKSIZE * (int)Animationprops[SETTING - 1, 4, 3], false, false);
                            this.state.interactiveBlocks.addBlock(new InvisBlock(this.mainSpriteBatch, new Vector2(x * CONST.BLOCKSIZE, y * CONST.BLOCKSIZE), new Vector2(CONST.BLOCKSIZE, CONST.BLOCKSIZE), s1InvisBlockIdle, s1InvisBlockTriggered));
                            break;
                        default: curLevel[x, y] = FieldType.Nothing; break;
                    }
                }
            }
            this.initTexturesAnimations();
        }
        // Print an Overview

        public List<string> printOverview()
        {
            List<string> bA = WebLoader.loadAllMapsNameOnly();
            foreach (string value in bA)
            {
                Console.WriteLine(value);
            }
            return bA;
        }

        // Draw the World

        public void drawWorld(GameTime gameTime, Vector2 curZero)
        {
            // Draw Standard Stuff

            for (int x = 0; x < this.XCOUNT; x++)
            {
                for (int y = 0; y < this.YCOUNT; y++)
                {
                    int newX = x * CONST.BLOCKSIZE - (int)curZero.X;
                    int newY = y * CONST.BLOCKSIZE - (int)curZero.Y;

                    switch (this.curLevel[x, y])
                    {
                        case FieldType.Goal:
                            //mainSpriteBatch.Draw(s1GoalTexture, new Rectangle(newX, newY - CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE * 2), new Rectangle(0, 0, CONST.BLOCKSIZE, CONST.BLOCKSIZE * 2), Color.White);
                            break;
                        case FieldType.simpleBlock1:
                            if (!isUpperEmpty(x, y)) mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(0, 0, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White);
                            else mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White);
                            break;
                        case FieldType.simpleBlock2:
                            if (!isUpperEmpty(x, y)) mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(CONST.BLOCKSIZE, 0, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White);
                            else mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White); 
                            break;
                        case FieldType.simpleBlock3:
                            if (!isUpperEmpty(x, y)) mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(0, CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White);
                            else mainSpriteBatch.Draw(s1SimpleBlockTexture, new Rectangle(newX, newY, CONST.BLOCKSIZE, CONST.BLOCKSIZE), new Rectangle(CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE, CONST.BLOCKSIZE), Color.White);
                            break;
                    }
                }
            }
        }

        
        // Check if Tile above is FieldType.Nothing

        private bool isUpperEmpty(int x, int y)
        {
            if (y - 1 < 0) return false; // OutOfBounds
            if (this.curLevel[x, y - 1] == FieldType.simpleBlock1 || this.curLevel[x, y - 1] == FieldType.simpleBlock2 || this.curLevel[x, y - 1] == FieldType.simpleBlock3) return false; // There is nothing
            return true;
        }

        // Init Textures

        public void initTexturesAnimations()
        {
                    s1SimpleBlockTexture = this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting" + SETTING + @"\simpleblock");

            
            //s1GoalTexture = this.state.getGame().Content.Load<Texture2D>(@"Textures\Settings\Setting1\goal");
        }
    }

    /**
     * Enum with all types of blocks
     * */

    enum FieldType
    {
        Nothing, // 00
        Startpoint, // 01
        Goal, // 02
        simpleBlock1, // 10 v01
        simpleBlock2, // 10 v02
        simpleBlock3, // 10 v03
        dissolvingBlock, // 11
        spikeBlock, // 12
        invisBlock, // 13
    }
}